/**
 * https://www.tslang.cn/docs/handbook/classes.html
 * 类
 * 
 * 传统的JavaScript程序使用函数和基于原型的继承来创建可重用的组件,
 * 但对于熟悉使用面向对象方式的程序员来讲就有些棘手, 
 * 因为他们用的是基于类的继承并且对象是由类构建出来的。
 */

/**
 * 如果你使用过C#或Java, 你会对这种语法非常熟悉。
 * 我们声明一个`Greeter`类。这个类有3个成员: 
 * 一个`greeting`属性, 一个构造函数和一个`greet`方法。
 * 
 * 你会注意到, 我们在引用任何一个类成员的时候都用了`this`。 
 * 它表示我们访问的是类的成员。
 * 
 * 
 */
class Greeter {
    greeting: string;
    constructor(message: string) {
        this.greeting = message;
    }
    greet(){
        return "Hello, " + this.greeting;
    }
}

let greeter = new Greeter("world");

class Animal102 {
    move(distanceInMeters: number = 0) {
        console.log(`Animal moved ${distanceInMeters}m.`);
    }
}
class Dog102 extends Animal102 {
    bark(){
        console.log('Woof! Woof!');
    }
}

const dog = new Dog102();
dog.bark();
dog.move(10);
dog.bark();

class Animal103 {
    name: string;
    constructor(theName: string) {this.name = theName;}
    move(distanceInMeters: number = 0){
        console.log(`${this.name} moved ${distanceInMeters}m.`);
    }
}

class Snake103 extends Animal103 {
    constructor(name: string) { super(name); }
    move(distanceInMeters = 5){
        console.log("Slithering...");
        super.move(distanceInMeters);
    }
}
class Horse103 extends Animal103 {
    constructor(name: string) { super(name); }
    move(distanceInMeters = 45){
        console.log("Galloping...");
        super.move(distanceInMeters);
    }
}
let sam = new Snake103("Sammy the Python");
let tom: Animal103 = new Horse103("Tommy the Palomino");

sam.move();
tom.move(34);

class Animal104 {
    private name: string;
    constructor(theName: string){
        this.name = theName;
    }
}

// new Animal104("Cat").name; // Error: 'name'是私有的

class Animal105 {
    private name: string;
    constructor(theName: string){
        this.name = theName;
    }
}
class Rhino extends Animal105 {
    constructor(){
        super("Rhino");
    }
}
class Employee {
    private name: string;
    constructor(theName: string) {
        this.name = theName;
    }
}

let animal105 = new Animal105("Goat");
let rhino = new Rhino();
let employee = new Employee("Bob");

animal105 = rhino;
// animal105 = employee; // 错误: Animla 与 Employee 不兼容.

class Person {
    protected name: string;
    constructor(name: string) {
        this.name = name;
    }
}

class Employee102 extends Person {
    private department: string;
    constructor(name:string, department:string){
        super(name);
        this.department = department;
    }
    public getElevatorPitch(){
        return `Hello, my name is ${this.name} and I work in ${this.department}.`;
    }
}

let howard = new Employee102("Howard", "Sales");
console.log(howard.getElevatorPitch());
// console.log(howard.name); //错误

class Person103 {
    protected name: string;
    protected constructor(theName: string){
        this.name = theName;
    }
}
class Employee103 extends Person103 {
    private department: string;
    constructor(name: string, department:string){
        super(name);
        this.department = department;
    }
    public getElevatorPitch() {
        return `Hello, my name is ${this.name} and I work in ${this.department}.`;
    }
}

let howard103 = new Employee103("Howard", "Sales");
// let john = new Person103("John"); // 错误: 'Person'的构造函数是被保护的。

class Octopus {
    readonly name: string;
    readonly numberOfLegs: number = 8;
    constructor(theName: string){
        this.name = theName;
    }
}
let dad = new Octopus("Man with the 8 strong legs");
// dad.name = "Man with the 3-piece suit"; // 错误! name 是只读的.

/**
 * 参数属性
 * 
 * "参数属性"可以方便地让我们在一个地方定义并初始化一个成员。
 * 
 * 仅在构造函数里使用 `private name: string`参数来创建和初始化`name`成员。
 * 我们把声明和赋值合并至一处。
 * 
 */
class Animal107 {
    constructor(private name: string){}
    move(distanceInMeters: number){
        console.log(`${this.name} moved ${distanceInMeters}m.`);
    }
}

class Employee104 {
    fullName: string;
}
let employee104 = new Employee104();
employee104.fullName = "Bob Smith";
if(employee104.fullName){
    console.log(employee104.fullName);
}

let passcode = "secret passcode";
// class Employee105 {
//     private _fullName: string;
//     get fullName(): string {
//         return this._fullName;
//     }
//     set fullName(newName: string) {
//         if (passcode && passcode == "secret passcode"){
//             this._fullName = newName;
//         }else {
//             console.log("Error: Unauthorized update of employee!");
//         }
//     }
// }

/**
 * 静态属性
 * 
 * 
 */

class Grid {
    static origin = {x: 0, y: 0};
    calcualteDistanceFromOrigin(point: {x:number,y:number}){
        let xDist = (point.x - Grid.origin.x);
        let yDist = (point.y - Grid.origin.y);
        return Math.sqrt(xDist * xDist + yDist * yDist) / this.scale;
    }
    constructor (public scale: number) {}
}
let grid1 = new Grid(1.0); // 1x scale
let grid2 = new Grid(5.0); // 5x scale

console.log(grid1.calcualteDistanceFromOrigin({x:10, y:10}));
console.log(grid2.calcualteDistanceFromOrigin({ x: 10, y: 10 }));

/**
 * 抽象类
 */

abstract class Animal108 {
    abstract makeSound(): void;
    move(): void {
        console.log('roaming the earch...');
    }
}

abstract class Department {
    constructor(public name: string){}
    printName(): void {
        console.log('Department name: ' + this.name);
    }
    abstract printMeeting(): void; // 必须在派生类中实现
}
class AccountingDepartment extends Department {
    constructor() {
        super('Accounting and Auditing'); // 在派生类的构造函数中必须调用`super()`
    }
    printMeeting(): void {
        console.log('The Accounting Department meets each Monday at 10am.');
    }
    generateReports(): void {
        console.log('Generating accounting reports...');
    }
}

let department: Department; // 允许创建一个对抽象类型的引用
// department = new Department(); // 错误: 不能创建一个抽象类的实例
department = new AccountingDepartment(); // 允许对一个抽象子类进行实例化和赋值
department.printName();
department.printMeeting();
// department.generateReports(); // 错误: 方法在声明的抽象类中不存在

class Greeter103 {
    greeting: string;
    constructor(message: string){
        this.greeting = message;
    }
    greet(){
        return "Hello, " + this.greeting;
    }
}
let greeter103: Greeter103;
greeter103 = new Greeter103("world");
console.log(greeter103.greet());

class Greeter104 {
    static standardGreeting = "Hello, there";
    greeting: string;
    greet(){
        if(this.greeting){
            return "Hello, " + this.greeting;
        }else{
            return Greeter104.standardGreeting;
        }
    }
}

let greeter104 : Greeter104;
greeter104 = new Greeter104();
console.log(greeter104.greet());

let greeterMaker: typeof Greeter104 = Greeter104;
greeterMaker.standardGreeting = "Hey there!";

let greeter2: Greeter104 = new greeterMaker();
console.log(greeter2.greet());

/**
 * 把类当做接口使用
 * 
 * 类定义会创建两个东西: 类的实例类型和一个构造函数。
 * 因为类可以创建出类型, 所以你能够在允许使用接口的地方使用类。
 */
class Point102 {
    x: number;
    y: number;
}
interface Point3d extends Point102 {
    z: number;
}
let point3d: Point3d = {x:1, y:2, z: 3};






